/*
 * TrackUtils.java
 */
package behavior_analysis_control;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import au.com.bytecode.opencsv.CSVReader;
import behavior_analysis_model.MapGenerator;
import behavior_analysis_model.Progress;
import behavior_analysis_model.Track;

/**
 * TrackUtils is a class that contains static methods to create
 * Track objects from Strings.
 * 
 * @author Sean Brown
 * @version 11/5/2012
 */
public class TrackUtils {

	/**
	 * Pattern matcher for Date.
	 */
	private static final Pattern DATE = Pattern.compile("(\\d{1,2})/(\\d{1,2})/(\\d{4})");
	//private static final Pattern DATE = Pattern.compile("(0?[1-9]|1[012])/(0?[1-9]|[12][0-9]|3[01])/(\\d{4})");
	
	/**
	 * Pattern matcher for Time.
	 */
	private static final Pattern TIME = Pattern.compile("(1[012]|[1-9]):([0-5][0-9]):([0-5][0-9])\\s?(?i)(am|pm)");
	
	/**
	 * Static method that parses a List of String arrays and returns
	 * a list of valid Track objects created from that list's data.
	 * 
	 * @param entries List<String> containing all entries.
	 * @return List<Track> of all tracks.
	 * @throws DialogErrorException Thrown when there are invalid entries.
	 */
	@SuppressWarnings("unchecked")
	public static void CSVToTracks(final String the_filename,
			                       final List<Track> the_tracks,
			                       final Progress progress) throws DialogErrorException {
		final CSVReader reader;
		try {
			reader = new CSVReader(new FileReader(the_filename));
		} catch (final FileNotFoundException e) {
			throw new DialogErrorException("The file " + the_filename + " does not exist.\n");
		}
		final List<String[]> entries;
		try {
			entries = reader.readAll();
		} catch (final IOException e) {
			throw new DialogErrorException("The file could not be read.\n");
		}
		the_tracks.clear();
		final StringBuilder sb = new StringBuilder();
		//validate the header line
		final String[] header = entries.get(0);
		if (header[0].equals("Date (month/day/year)") &&
				header[1].equals("Time (UTC)") &&
				header[2].equals("Latitude (degrees)") &&
				header[3].equals("Longitude (degrees)")) {
			entries.remove(0);
		} else {
			sb.append("The file header is incorrect.\n");
			//pass through to validate as an entry
		}
		//check each line
		for (int i = 0; i < entries.size(); i++) {
			if (progress != null) {
				progress.setProgress(i * 100 / entries.size());
			}
			boolean invalid_entry = false;
			int day = 0;
			int month = 0;
			int year = 0;
			int hour = 0;
			int min = 0;
			int sec = 0;
			double longitude = 0.0;
			double latitude = 0.0;
			String[] entry = entries.get(i);
			if (entry.length != 4) {
				//skip this entry
				invalid_entry = true;
			} else {
				final String date = entry[0];
				//check characters with regex
				final Matcher date_matcher = DATE.matcher(date);
				if (date_matcher.matches() && date_matcher.groupCount() == 3) {
					//create temp values
					month = Integer.parseInt(date_matcher.group(1));
					day = Integer.parseInt(date_matcher.group(2));
					year = Integer.parseInt(date_matcher.group(3));
				} else {
					invalid_entry = true;
				}
				final String time = entry[1];
				//check characters
				final Matcher time_matcher = TIME.matcher(time);
				if (time_matcher.matches() && time_matcher.groupCount() == 4) {
					hour = Integer.parseInt(time_matcher.group(1));
					min = Integer.parseInt(time_matcher.group(2));
					sec = Integer.parseInt(time_matcher.group(3));
					final String ampm = time_matcher.group(4);
					//adjust time for AM/PM to 0-23 (military)
					if (ampm.equalsIgnoreCase("PM")) {
						if (hour != 12) {
							hour = hour + 12;
						}
					} else {//ampm is AM, from matcher
						if (hour == 12) {
							hour = 0;
						}
					}
				} else {
					invalid_entry = true;
				}
				//create doubles
				try {
				latitude = Double.valueOf(entry[2]);
				longitude = Double.valueOf(entry[3]);
				} catch (final NumberFormatException nfe) {
					invalid_entry = true;
				}
				try {
					DateUtils.validateCoords(latitude, longitude);
				} catch (final DialogErrorException the_e) {
					invalid_entry = true;
				}
				//create double object
			}
			if (invalid_entry) {
				sb.append("Entry on line " + i + " is invalid.\n");
			} else {
				//put track into array
				final long time = DateUtils.dateToMillis(year, month, day, hour, min, sec);
				the_tracks.add(new Track(latitude, longitude, time));
			}
		}
		if (sb.length() != 0) {
			throw new DialogErrorException(sb.toString());
		}
	}

	//public static List<Track> GPXToTracks(final String the_filename) throws DialogErrorException {
		
	//}
	
	public static String getAddress(final Track the_track) throws POINotFoundException {
		String result = "";
	    double lat = the_track.getLatitude();
	    double lon = the_track.getLongitude();
		
		DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder;
		try {
			builder = f.newDocumentBuilder();
			URL url = new URL("http://maps.google.com/maps/api/geocode/xml?latlng=" + lat + "," + lon + "&sensor=true");
			Document doc = builder.parse(url.openStream());
		    NodeList nl = doc.getElementsByTagName("formatted_address");
		    Element n = (Element)nl.item(0);
		    result = n.getFirstChild().getNodeValue();
		} catch (final Exception e) {
			throw new POINotFoundException("The address for this location is not available.");
		}
	    return result;
	}
	
	public static String getBusinessName(final Track the_track) throws POINotFoundException {
		String result = "";
	    double lat = the_track.getLatitude();
	    double lon = the_track.getLongitude();
		
		DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
		try {
			DocumentBuilder builder = f.newDocumentBuilder();
			URL url = new URL("https://maps.googleapis.com/maps/api/place/nearbysearch/xml?location=" + lat + "," + lon + 
					"&types=accounting|airport|amusement_park|aquarium|art_gallery|atm|bakery|bank|bar|beauty_salon|" + 
					"bicycle_store|book_store|bowling_alley|bus_station|cafe|campground|car_dealer|car_rental|car_repair|" + 
					"car_wash|casino|cemetery|church|city_hall|clothing_store|convenience_store|courthouse|dentist|department_store|" +
					"doctor|electrician|electronics_store|embassy|establishment|finance|fire_station|florist|food|funeral_home|" + 
					"furniture_store|gas_station|general_contractor|grocery_or_supermarket|gym|hair_care|hardware_store|health|" + 
					"hindu_temple|home_goods_store|hospital|insurance_agency|jewelry_store|laundry|lawyer|library|liquor_store|" + 
					"local_government_office|locksmith|lodging|meal_delivery|meal_takeaway|mosque|movie_rental|movie_theater|moving_company|" + 
					"museum|night_club|painter|park|parking|pet_store|pharmacy|physiotherapist|place_of_worship|plumber|police|post_office|" + 
					"real_estate_agency|restaurant|roofing_contractor|rv_park|school|shoe_store|shopping_mall|spa|stadium|storage|store|" + 
					"subway_station|synagogue|taxi_stand|train_station|travel_agency|university|veterinary_care|zoo&rankby=distance" + 
					"&sensor=true&key=" + MapGenerator.APIKEY);
			Document doc = builder.parse(url.openStream());
		    NodeList nl = doc.getElementsByTagName("name");
		    Element n = (Element)nl.item(0);
		    result = n.getFirstChild().getNodeValue();
		} catch (final Exception e) {
			throw new POINotFoundException("The address for this location is not available.");
		}
	    return result;
	}
}
